playwright(fix): test reliability — ES indexing, locator strict-mode, waitForResponse races#29585
playwright(fix): test reliability — ES indexing, locator strict-mode, waitForResponse races#29585chirag-madlani wants to merge 10 commits into
Conversation
… waitForResponse races
Bundle of test-side bug fixes for races and locator issues in existing
Playwright E2E specs. No config, no test-file moves, no behavior change
to the system under test — just makes flaky assertions deterministic.
ES indexing races (entities created via API, then read by UI before
search index updates):
- BulkEditEntity.spec.ts: wait for glossary_term_search_index before
opening bulk-edit table (otherwise the term shows as "Entity created"
instead of "Entity updated")
- BulkEditOperationBadges.spec.ts: same pattern for the file's beforeAll
(term + table search indexes)
- BulkImport.spec.ts: wait for database_service / database_schema /
table search indexes; pre-wait .rdg-cell-details rendering before
asserting text (was racing the async grid populate)
- TestCaseImportExportE2eFlow.spec.ts: wait for test_case_search_index
so the just-created test case appears in the export's CSV
waitForResponse races (listener registered after the action, or matching
too-broad URL pattern that resolves on a stale earlier response):
- utils/searchRBAC.ts searchForEntityShouldWork[ShowNoResult]: register
before press('Enter')
- SearchIndexNestedColumns.spec.ts: predicate-match the URL that
contains the encoded search term (the empty-query suggestion fetch
was resolving the listener first)
Locator-stability / timeout fixes:
- Roles.spec.ts (×7 sites): replace strict-mode-violating
expect(loader).not.toBeVisible() with waitForAllLoadersToDisappear
(handles N loaders correctly)
- ExploreBrowse.spec.ts: explicit toBeVisible before clicking
explore-tree-title-mysql so the tree settles after expandTreeNode
- Table.spec.ts: wait for sort + next-page responses, replace
count()===15 with toHaveCount(15) auto-retry
Backend-propagation timeouts (the UI fetches lag the API write):
- utils/searchRBAC.ts exploreTreeCategories: wrap visibility/absence
checks in expect.toPass({ timeout: 20s }) — tree counts come from
multiple aggregation queries, single search-query wait is not enough
- utils/searchRBAC.ts exploreShouldShowEntity: bump negative-assertion
to 45s — RBAC filtering against newly-assigned roles lags the patch
by several seconds
- utils/taskWorkflow.ts openTaskDetails: 15s → 45s — activity-feed
refresh after API task create lags past default under load
Removed 7 unannotated `waitForTimeout` calls that were leaking past
ESLint and replaced with proper waits where applicable (Tasks/, Pages/
TaskComments + TasksUIFlow, ActivityStream, ActivityFeed). The 4 in
DomainUIInteractions / DataProductAndSubdomains were migrated to the
existing `waitForSearchIndexed` polling helper.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
❌ PR checklist incompleteThis PR cannot be merged until the following are addressed on its linked issue:
The fields live on the linked issue in the Shipping project (open the issue → right sidebar → Projects). After you set them, re-run this check (or push a commit) — issue/project changes do not re-trigger it automatically. Maintainers can bypass this check by adding the |
🔴 Playwright Results — 10 failure(s), 57 flaky✅ 4416 passed · ❌ 10 failed · 🟡 57 flaky · ⏭️ 38 skipped
Genuine Failures (failed on all attempts)❌
|
An empty FQN encoded into the q= parameter becomes a match-all query, which lets the helper return on the first poll against any non-empty index — silently bypassing the very indexing race it exists to close. Throw a clear error at the source instead, and drop the misleading `?? ''` fallback at call sites so the type contract reflects reality. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Code Review 👍 Approved with suggestions 2 resolved / 4 findingsHardens E2E tests against ES indexing races and locator timeouts by implementing robust polling and deterministic response handling. Address the remaining 💡 Quality: Closed-tab verification can silently no-op after reload📄 openmetadata-ui/src/main/resources/ui/playwright/e2e/Pages/TasksUIFlow.spec.ts:458-472 The 'Resolve task and verify it moves to Closed' step previously called Re-navigate to the tasks tab after reload and assert the Closed tab is present rather than conditionally skipping verification.💡 Edge Case: openEntityTasksTab drops the button-variant Tasks tab fallback📄 openmetadata-ui/src/main/resources/ui/playwright/utils/taskWorkflow.ts:357-371 The refactor of Preserve support for both the menuitem and button Tasks-tab variants.✅ 2 resolved✅ Quality: Unused
|
| Compact |
|
Was this helpful? React with 👍 / 👎 | Gitar
Describe your changes:
PR-A of a 4-way split of #29435. This PR contains only the test-side bug fixes — no config changes, no spec-file moves, no sharding shift. Each fix addresses a real race or locator issue in an existing Playwright E2E spec that was previously surviving on retries:2 but became visible under retry pressure.
The fixes fall into 4 categories:
ES indexing races — entity created via API, then read by UI before search index updates:
BulkEditEntity.spec.ts: wait forglossary_term_search_indexBulkEditOperationBadges.spec.ts: same pattern inbeforeAllBulkImport.spec.ts: wait fordatabase_service/database_schema/tableindexes; pre-wait.rdg-cell-detailscount before asserting textTestCaseImportExportE2eFlow.spec.ts: wait fortest_case_search_indexso the just-created test case is in the export CSVwaitForResponseraces — listener registered after the action, or matching a too-broad URL that resolves on a stale earlier response:utils/searchRBAC.tssearchForEntity helpers: register beforepress('Enter')SearchIndexNestedColumns.spec.ts: predicate-match the URL containing the encoded search termLocator stability / timeout fixes:
Roles.spec.ts(×7 sites): replace strict-mode-violatingexpect(loader).not.toBeVisible()withwaitForAllLoadersToDisappear(handles N loaders correctly)ExploreBrowse.spec.ts: explicittoBeVisiblebefore clicking tree title so the tree settles after expandTreeNodeTable.spec.ts: wait for sort + next-page responses, replacecount()===15withtoHaveCount(15)auto-retryBackend-propagation timeouts — UI fetches lag the API write:
utils/searchRBAC.ts exploreTreeCategories: wrap visibility/absence checks inexpect.toPass({ timeout: 20s })— tree counts come from multiple aggregation queriesutils/searchRBAC.ts exploreShouldShowEntity: bump negative-assertion to 45s — RBAC filtering against newly-assigned roles lags the patchutils/taskWorkflow.ts openTaskDetails: 15s → 45s — activity-feed refresh after API task create lags past default under loadAlso removed 7 unannotated
waitForTimeoutcalls leaking past ESLint, replaced with proper waits. The 4 in DomainUIInteractions / DataProductAndSubdomains migrated to the existingwaitForSearchIndexedpolling helper.Type of change:
High-level design:
Each fix is independent and self-contained. No shared helpers introduced; just makes existing tests' assertions deterministic against the system they're already testing. The PR is a clean 16-file diff (+214 / -49) with no file moves, deletes, or new tests.
Tests:
Pure test-side fixes. No new tests needed — each change makes an existing flaky assertion deterministic. Verified locally that prettier + organize-imports pass and that
tsc --noEmitintroduces no new errors (pre-existing TS errors on main are unaffected).Playwright (UI) tests
UI screen recording / screenshots:
Not applicable — no UI changes.
Checklist:
Split off from #29435 — this is the first of four progressively-merging PRs (test fixes → single-spec caps → config + dedup → PR/stress project split). No associated GitHub issue — this is internal test-tooling work.
🤖 Generated with Claude Code
Greptile Summary
This PR makes 16 Playwright E2E spec files more reliable by replacing
waitForTimeoutcalls and flaky assertion patterns with proper async synchronization primitives —waitForSearchIndexedpolling,waitForResponsepredicates,toHaveCount/toPass, and explicittoBeVisibleguards before clicks.BulkEditEntity,BulkEditOperationBadges,BulkImport,TestCaseImportExportE2eFlow, and the domain/subdomain specs) by insertingwaitForSearchIndexedcalls after API entity creation.waitForResponseraces fixed insearchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultby registering the listener betweenfillandpress('Enter');SearchIndexNestedColumnsupdated to match by URL predicate instead of a broad glob.Table.spec.ts(count() === N→toHaveCount(N)),ExploreBrowse.spec.ts(explicittoBeVisiblebefore tree-node click), andtaskWorkflow.ts(15 s → 45 s timeout onopenTaskDetails).Confidence Score: 4/5
exploreShouldShowEntitystill registers itswaitForResponselistener beforefill(), leaving it exposed to the autocomplete-response race that was explicitly fixed in the two sibling helpers in this same PR. All other changes are correct.exploreShouldShowEntityregisterssearchRes = page.waitForResponse('/api/v1/search/query?*')beforefill(fqn), so an autocomplete query fired while the user types can consume the listener beforepress('Enter')fires the real search — assertions then run against stale autocomplete UI. This is the identical race thatsearchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultwere fixed for in this very PR, but the fix was not carried over here.searchRBAC.ts—exploreShouldShowEntity(lines 48–51) needs the samefill-then-register-then-press ordering applied to the other two helpers in this file.Important Files Changed
searchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultcorrectly registerwaitForResponseafterfilland beforepress('Enter'), butexploreShouldShowEntitystill registerssearchResbeforefill()— the same race this PR set out to fix.q=match-all footgun; change is correct and well-commented.waitForon the specific suggestion testId; column-search listener upgraded to URL-predicate match — both fixes are sound.count() === 15withtoHaveCount(15)and addswaitForResponseguards before sort/next-page clicks; straightforward improvement.waitForSearchIndexedfor database service, schema, and table before bulk-import grid interactions, plus atoHaveCountpre-wait beforetoContainTexton result cells.toBeVisiblepoll before tree-node click and registerswaitForResponsebefore each click — correctly fixes tree-animation timing issue.openTaskDetailsvisibility timeout from 15 s to 45 s with clear comment; reasonable adjustment for feed-propagation lag under parallelism.waitForSearchIndexedfor the just-created test case in both Admin and non-AdminbeforeAllblocks before the export step reads from search.waitForTimeout(2000)calls in retry loops withwaitForSearchIndexedpolling; the.catch(() => undefined)suppression is intentional (soft retry).waitForTimeoutin retry loop withwaitForSearchIndexed; consistent and correct.Sequence Diagram
%%{init: {'theme': 'neutral'}}%% sequenceDiagram participant T as Test participant PW as Playwright participant UI as Browser UI participant API as Search API (ES) Note over T,API: searchForEntityShouldWork / searchForEntityShouldWorkShowNoResult (FIXED) T->>UI: fill(fqn) UI->>API: "autocomplete query /api/v1/search/query?q=..." API-->>UI: autocomplete response (ignored — listener not yet registered) T->>PW: waitForResponse registered HERE T->>UI: press('Enter') UI->>API: "search-results query /api/v1/search/query?q=..." API-->>PW: response captured ✓ PW-->>T: searchResponse resolved T->>UI: assert resultCard Note over T,API: exploreShouldShowEntity (STILL UNFIXED in this PR) T->>PW: waitForResponse registered TOO EARLY ⚠ T->>UI: fill(fqn) UI->>API: "autocomplete query /api/v1/search/query?q=..." API-->>PW: autocomplete response resolves searchRes prematurely ⚠ T->>UI: press('Enter') UI->>API: search-results query (listener already consumed) T->>UI: assert resultCard — runs against autocomplete state%%{init: {'theme': 'base', 'themeVariables': {"darkMode": true, "background": "#0d1117", "primaryColor": "#21262d", "primaryTextColor": "#e6edf3", "primaryBorderColor": "#8b949e", "lineColor": "#8b949e", "textColor": "#e6edf3", "edgeLabelBackground": "#161b22", "actorBkg": "#21262d", "actorBorder": "#8b949e", "actorTextColor": "#e6edf3", "actorLineColor": "#8b949e", "signalColor": "#8b949e", "signalTextColor": "#e6edf3", "noteBkgColor": "#373320", "noteBorderColor": "#d4a72c", "noteTextColor": "#f0e6c0", "labelBoxBkgColor": "#21262d", "labelBoxBorderColor": "#8b949e", "labelTextColor": "#e6edf3", "loopTextColor": "#e6edf3", "activationBkgColor": "#30363d", "activationBorderColor": "#8b949e"}}}%% sequenceDiagram participant T as Test participant PW as Playwright participant UI as Browser UI participant API as Search API (ES) Note over T,API: searchForEntityShouldWork / searchForEntityShouldWorkShowNoResult (FIXED) T->>UI: fill(fqn) UI->>API: "autocomplete query /api/v1/search/query?q=..." API-->>UI: autocomplete response (ignored — listener not yet registered) T->>PW: waitForResponse registered HERE T->>UI: press('Enter') UI->>API: "search-results query /api/v1/search/query?q=..." API-->>PW: response captured ✓ PW-->>T: searchResponse resolved T->>UI: assert resultCard Note over T,API: exploreShouldShowEntity (STILL UNFIXED in this PR) T->>PW: waitForResponse registered TOO EARLY ⚠ T->>UI: fill(fqn) UI->>API: "autocomplete query /api/v1/search/query?q=..." API-->>PW: autocomplete response resolves searchRes prematurely ⚠ T->>UI: press('Enter') UI->>API: search-results query (listener already consumed) T->>UI: assert resultCard — runs against autocomplete stateComments Outside Diff (6)
openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)waitForResponsestill registered beforefill()inexploreShouldShowEntitysearchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultwere fixed in this PR to register the listener betweenfillandpress('Enter')specifically becausefillcan trigger an autocomplete/suggestion query that resolves the broadwaitForResponse('/api/v1/search/query?*')listener prematurely.exploreShouldShowEntitystill registerssearchResbeforefill()on line 48, leaving it exposed to the same race: if the search box fires a suggestion fetch while the user is typing,searchResresolves on that response andpress('Enter')'s actual result page is never waited for, so theresultCardassertion can run against stale UI.openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)waitForResponsestill registered beforefill()inexploreShouldShowEntitysearchResis registered beforefill(fqn)on line 48. If the search box fires an autocomplete/suggestion query while the user is typing,searchResresolves on that early response and thepress('Enter')result page is never awaited — so theresultCardassertion runs against stale UI. This is the exact race that was deliberately fixed insearchForEntityShouldWork(register afterfill, beforepress) but the same pattern was left unremedied here.openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)searchResis registered beforefill(fqn)(line 49), so if the search box fires an autocomplete/suggestion query while the user types, that early response resolves the listener and thepress('Enter')result-page response is never awaited — theresultCardassertion then runs against stale UI. This is exactly the race fixed insearchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultin this same PR: register the listener afterfill, beforepress.openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)searchResis registered beforefill(fqn)on line 48. Thefill()call on line 49 can immediately trigger an autocomplete/suggestion query whose URL matches'/api/v1/search/query?*', resolving the listener beforepress('Enter')fires the actual results-page query. The assertion then runs against stale autocomplete results rather than the real search page. This is the exact race that was deliberately fixed insearchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultin this same PR — register afterfill, beforepress.openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)waitForResponseregistered beforefill()— same race left unfixedsearchRes(line 48) is created beforefill(fqn)executes on line 49. Typing into the search box triggers autocomplete queries whose URL matches'/api/v1/search/query?*', which can resolvesearchReson that autocomplete response beforepress('Enter')fires the actual search-page query. When that happens thewaitForAllLoadersToDisappearand theresultCardassertion run against the autocomplete-populated UI, not the search-results page — sotoHaveCount(0, { timeout: 45_000 })for the negative path may pass or fail spuriously.searchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultwere fixed in this very PR to move the listener registration to afterfilland beforepress('Enter')for exactly this reason. The same fix is needed here.openmetadata-ui/src/main/resources/ui/playwright/utils/searchRBAC.ts, line 48-51 (link)searchResis registered beforefill(fqn)on line 48. Typing into the search box triggers autocomplete/suggestion queries whose URL matches'/api/v1/search/query?*', which resolvessearchReson that early response beforepress('Enter')fires the actual search-results-page query. ThewaitForAllLoadersToDisappearandresultCardassertions then run against stale autocomplete UI —toHaveCount(0, { timeout: 45_000 })on the negative path can pass or fail spuriously.This is the identical race that was deliberately fixed in
searchForEntityShouldWorkandsearchForEntityShouldWorkShowNoResultin this same PR (register afterfill, beforepress). The fix should be applied here too.Reviews (8): Last reviewed commit: "Merge branch 'main' into playwright-test..." | Re-trigger Greptile